---
title: Adapters
description: Using nuqs in your React framework of choice
---

import {
  NextJS,
  ReactRouter,
  ReactRouterV7,
  ReactSPA,
  Remix,
  TanStackRouter,
  Vitest,
} from '@/src/components/frameworks'

Since version 2, you can now use nuqs in the following React frameworks, by
wrapping it with a `NuqsAdapter{:ts}` context provider:

- <NextJS className='inline mr-1.5' role="presentation"/> [Next.js (app router)](#nextjs-app-router)
- <NextJS className='inline mr-1.5' role="presentation"/> [Next.js (pages router)](#nextjs-pages-router)
- <ReactSPA className='inline mr-1.5' role="presentation" /> [React SPA (eg: with Vite)](#react-spa)
- <Remix className='inline mr-1.5' role="presentation" /> [Remix](#remix)
- <ReactRouter className='inline mr-1.5' role="presentation" /> [React Router v6](#react-router-v6)
- <ReactRouterV7 className='inline mr-1.5' role="presentation" /> [React Router v7](#react-router-v7)
- <TanStackRouter className='inline mr-1.5 not-prose' role="presentation"/> [TanStack Router](#tanstack-router)

## <NextJS className='inline mr-1.5 -mt-1' role="presentation" /> Next.js [#nextjs]

### App router [#nextjs-app-router]

Wrap your `{children}{:ts}` with the `NuqsAdapter{:ts}` component in your root layout file:

```tsx title="src/app/layout.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/next/app'
import { type ReactNode } from 'react'

export default function RootLayout({
  children
}: {
  children: ReactNode
}) {
  return (
    <html>
      <body>
        <NuqsAdapter>{children}</NuqsAdapter>
      </body>
    </html>
  )
}
```

### Pages router [#nextjs-pages-router]

Wrap the `<Component>{:ts}` page outlet with the `NuqsAdapter{:ts}` component in your `_app.tsx` file:

```tsx title="src/pages/_app.tsx"
// [!code word:NuqsAdapter]
import type { AppProps } from 'next/app'
import { NuqsAdapter } from 'nuqs/adapters/next/pages'

export default function MyApp({ Component, pageProps }: AppProps) {
  return (
    <NuqsAdapter>
      <Component {...pageProps} />
    </NuqsAdapter>
  )
}
```

### Unified (router-agnostic) [#nextjs-unified]

If your Next.js app uses **both the app and pages routers** and the adapter needs
to be mounted in either, you can import the unified adapter, at the cost
of a slightly larger bundle size (~100B).

```tsx
import { NuqsAdapter } from 'nuqs/adapters/next'
```

<br/>

The main reason for adapters is to open up nuqs to other React frameworks:

## <ReactSPA className='inline mr-1.5 -mt-1' role="presentation"/> React SPA [#react-spa]

Example, with Vite:

```tsx title="src/main.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/react'

createRoot(document.getElementById('root')!).render(
  <NuqsAdapter>
    <App />
  </NuqsAdapter>
)
```

<Callout title="Note">
Because there is no known server in this configuration, the
[`shallow: false{:ts}`](/docs/options#shallow) option will have no effect.

See below for some options:
</Callout>

### Full page navigation on <br className='hidden [#nd-toc_&]:block'/>`shallow: false{:ts}` [#full-page-navigation-on-shallow-false]

<FeatureSupportMatrix introducedInVersion='2.4.0' support={{
  supported: true,
  frameworks: ['React SPA']
}}/>

You can specify a flag to perform a full-page navigation when
updating query state configured with `shallow: false{:ts}`, to notify the web server
that the URL state has changed, if it needs it for server-side rendering other
parts of the application than the static React bundle:

```tsx title="src/main.tsx"
// [!code word:fullPageNavigationOnShallowFalseUpdates]
createRoot(document.getElementById('root')!).render(
  <NuqsAdapter fullPageNavigationOnShallowFalseUpdates>
    <App />
  </NuqsAdapter>
)
```

This may be useful for servers not written in JavaScript, like Django (Python),
Rails (Ruby), Laravel (PHP), Phoenix (Elixir) etc...

## <Remix className='inline mr-1.5 -mt-1' role="presentation"/> Remix [#remix]

```tsx title="app/root.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/remix'

// ...

export default function App() {
  return (
    <NuqsAdapter>
      <Outlet />
    </NuqsAdapter>
  )
}
```

## <ReactRouter className='inline mr-1.5 -mt-1' role="presentation"/> React Router v6 [#react-router-v6]

```tsx title="src/main.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/react-router/v6'
import { createBrowserRouter, RouterProvider } from 'react-router-dom'
import App from './App'

const router = createBrowserRouter([
  {
    path: '/',
    element: <App />
  }
])

export function ReactRouter() {
  return (
    <NuqsAdapter>
      <RouterProvider router={router} />
    </NuqsAdapter>
  )
}
```

<Callout>

 Only `BrowserRouter` is supported. There may be support for `HashRouter`
 in the future (see issue [#810](https://github.com/47ng/nuqs/issues/810)), but
 support for `MemoryRouter` is not planned.

</Callout>

## <ReactRouterV7 className='inline mr-1.5 -mt-1' role="presentation"/> React Router v7 [#react-router-v7]

```tsx title="app/root.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/react-router/v7'
import { Outlet } from 'react-router'

// ...

export default function App() {
  return (
    <NuqsAdapter>
      <Outlet />
    </NuqsAdapter>
  )
}
```

<Callout type="warn" title="Deprecation notice">
  The generic import `nuqs/adapters/react-router` (pointing to v6)
  is deprecated and will be removed in nuqs@3.0.0.

  Please pin your imports to the specific version,
  eg: `nuqs/adapters/react-router/v6` or `nuqs/adapters/react-router/v7`.

  The main difference is where the React Router hooks are imported from:
  `react-router-dom` for v6, and `react-router` for v7.
</Callout>

## <TanStackRouter className='inline mr-1.5 -mt-1 not-prose' role="presentation"/> TanStack Router [#tanstack-router]

```tsx title="src/routes/__root.tsx"
// [!code word:NuqsAdapter]
import { NuqsAdapter } from 'nuqs/adapters/tanstack-router'
import { Outlet, createRootRoute } from '@tanstack/react-router'

export const Route = createRootRoute({
  component: () => (
    <>
      <NuqsAdapter>
        <Outlet />
      </NuqsAdapter>
    </>
  ),
})
```

<Callout>

TanStack Router support is experimental and does not yet cover TanStack Start.

</Callout>

### Type-safe routing via `validateSearch`

TanStack Router comes with built-in type-safe search params support,
and its APIs should likely be used in your application code for best DX.

Nevertheless, sometimes you may need to import a component that uses nuqs
(from NPM or a shared library), and benefit from TanStack Router's type-safe routing.

You may do so via the [Standard Schema](/docs/utilities#standard-schema) support:

```tsx
import { createFileRoute, Link } from '@tanstack/react-router'
import {
  createStandardSchemaV1,
  parseAsIndex,
  parseAsString,
  useQueryStates
} from 'nuqs'

const searchParams = {
  searchQuery: parseAsString.withDefault(''),
  pageIndex: parseAsIndex.withDefault(0),
}

export const Route = createFileRoute('/search')({
  component: RouteComponent,
  // [!code highlight:3]
  validateSearch: createStandardSchemaV1(searchParams, {
    partialOutput: true
  })
})

function RouteComponent() {
  // Consume nuqs state as usual:
  const [{ searchQuery, pageIndex }] = useQueryStates(searchParams)
  // But now TanStack Router knows about it too:
  return (
    <Link
      to="/search"
      search={{
        searchQuery: 'foo',
        // note: we're not specifying pageIndex
      }}
    />
  )
}
```

Note that the `partialOutput{:ts}` flag allows specifying only a subset of
the search params for a given route. It also does not reflect those search
in the URL automatically, following nuqs' behaviour more closely.

<Callout title="Caveats" type="warn">

Due to differences in how TanStack Router and nuqs handle serialisation and deserialisation
(global in TanStack Router and per-key in nuqs), only _trivial_ state types are supported for
type-safe linking. Those include all string-based parsers (string, enum, literals),
number-based (integer, float, number literal), boolean, and JSON.

The `urlKeys` feature to provide shorthand key names is also not supported for similar
reasons.

</Callout>

## <Vitest className='inline mr-1.5 -mt-1' role="presentation"/> Testing

<Callout>
  Documentation for the `NuqsTestingAdapter{:ts}` is on the [testing page](/docs/testing).
</Callout>
